boto3と1Passwordを使って、AWSのMFA認証とスイッチロールを自動化してみた!
こんにちは!今回は、AWSのMFA(Multi-Factor Authentication)認証を使い、IAMロールにスイッチしながらS3バケットにアクセスするPythonスクリプトを紹介します。このスクリプトは、AWSのセキュリティベストプラクティスに従い、MFAを使用して一時的なセッションを取得し、S3バケットのリストを取得する方法を示しています。
さらに、1Passwordを活用してワンタイムパスワード(OTP)を自動的に取得する仕組みも組み込んでいるため、非常に便利で安全なアクセスが可能です。AWSのセキュリティを強化したい方や、MFA認証を自動化したい方に役立つ内容になっています。
背景
AWSでは、セキュリティを強化するためにMFAを使用することが推奨されています。MFAを使用すると、ユーザーは通常の認証情報(アクセスキーやシークレットキー)に加えて、ワンタイムパスワード(OTP)を入力する必要があります。これにより、認証情報が漏洩した場合でも、攻撃者がアクセスするのを防ぐことができます。
さらに、AWSのAssumeRole機能を使用することで、特定のIAMロールに一時的にスイッチして、そのロールの権限でAWSリソースにアクセスできます。これにより、最小限の権限を付与しつつ、必要な操作を行うことが可能です。
今回紹介するスクリプトでは、これらのセキュリティ機能を組み合わせて、MFA認証を行いながらIAMロールにスイッチし、S3バケットにアクセスする方法を実装しています。
スクリプトの全体像
まずは、スクリプトの全体像を見てみましょう。
import os
import boto3
from boto3.session import Session
from botocore.config import Config
from onepassword import OnePassword
# IAMユーザー名を指定(このユーザーのMFAデバイスを使用する)
IAM_USER_NAME = os.getenv("IAM_USER_NAME")
# AssumeRoleする際に使用するターゲットのAWS IAMロールARN
TARGET_ROLE_ARN = os.getenv("TARGET_ROLE_ARN")
# 1Passwordで保管されているAWSのアクセスキーに関連するタイトル
TITLE = '<AWS Access Key>'
# AWSのリージョン(ここでは東京リージョンを使用)
TARGET_REGION = 'ap-northeast-1'
def get_one_time_password() -> int:
"""
1Passwordからワンタイムパスワード(OTP)を取得する関数。
このOTPはMFA認証時に使用される。
Returns:
int: ワンタイムパスワード
"""
# 1Passwordのインスタンスを作成
op = OnePassword()
# 1Password内のアイテムリストから、指定したタイトル(TITLE)に一致するアイテムを取得し、そのUUIDを取得
uuid = [item['id'] for item in op.list_items() if item['title'] == TITLE][0]
# 取得したUUIDを使って、OTP(ワンタイムパスワード)を取得
one_time_password = op.get_item_otp(uuid=uuid)
return one_time_password
def get_mfa_device() -> str:
"""
IAMユーザーに紐づけられたMFAデバイスのシリアル番号を取得する関数。
このシリアル番号はMFA認証時に使用される。
Returns:
str: MFAデバイスのシリアル番号
"""
# IAMクライアントを作成
client = boto3.client("iam")
# 指定したIAMユーザーに紐づけられたMFAデバイスのリストを取得し、最初のデバイスのシリアル番号を取得
mfa_device = client.list_mfa_devices(UserName=IAM_USER_NAME)['MFADevices'][0]['SerialNumber']
return mfa_device
def assume_role(one_time_password: int, mfa_device: str, TARGET_ROLE_ARN: str) -> Session:
"""
指定したロールにスイッチロール(Assume Role)する関数。
MFA認証を行い、一時的なセッションを取得する。
Args:
one_time_password (int): ワンタイムパスワード(OTP)
mfa_device (str): MFAデバイスのシリアル番号
TARGET_ROLE_ARN (str): スイッチロールするターゲットロールのARN
Returns:
Session: 一時的なセッション情報を持つboto3のSessionオブジェクト
"""
# STSクライアントを作成
client = boto3.client("sts")
# AssumeRole APIを呼び出して、一時的な認証情報を取得
temp_credentials = client.assume_role(
RoleArn=TARGET_ROLE_ARN, # スイッチするロールのARN
RoleSessionName='assume_role_sample', # セッションに付ける名前
SerialNumber=mfa_device, # MFAデバイスのシリアル番号
TokenCode=one_time_password # ワンタイムパスワード(OTP)
)
# 一時的な認証情報を使用して新しいセッションを作成
assume_role_session = Session(
aws_access_key_id=temp_credentials['Credentials']['AccessKeyId'],
aws_secret_access_key=temp_credentials['Credentials']['SecretAccessKey'],
aws_session_token=temp_credentials['Credentials']['SessionToken'],
region_name=TARGET_REGION, # 使用するリージョン
)
return assume_role_session
def main():
"""
メイン処理を行う関数。
MFA認証を行い、指定のロールにスイッチロールした後、S3のバケットリストを表示する。
"""
# MFAデバイスのシリアル番号を取得
mfa_device = get_mfa_device()
# 1Passwordからワンタイムパスワードを取得
one_time_password = get_one_time_password()
# スイッチロールを行い、一時的なセッションを取得
assume_role_session = assume_role(one_time_password, mfa_device, TARGET_ROLE_ARN)
# スイッチロールしたセッションを使ってS3クライアントを作成
s3 = assume_role_session.client('s3')
# S3バケットのリストを取得して表示
print(s3.list_buckets())
# メイン処理を実行
if __name__ == "__main__":
main()
スクリプトの流れ
1. 1Passwordからワンタイムパスワードを取得
まず、get_one_time_password
関数を使って、1Passwordからワンタイムパスワード(OTP)を取得します。このOTPはMFA認証時に必要です。
2. IAMユーザーのMFAデバイスを取得
get_mfa_device
関数を使って、指定したIAMユーザーに関連するMFAデバイスのシリアル番号を取得します。これは、MFA認証を行うために必要です。
3. AssumeRoleでIAMロールにスイッチ
assume_role
関数を使って、STSのAssumeRole APIを呼び出し、指定したIAMロールにスイッチします。ここで、MFA認証を行うために、先ほど取得したOTPとMFAデバイスのシリアル番号を使用します。
4. S3バケットのリストを取得
スイッチロールができていることを確認するため、今回は、S3バケットのリストを取得します。
詳細解説
1PasswordからのOTP取得
def get_one_time_password() -> int:
"""
1Passwordからワンタイムパスワード(OTP)を取得する関数。
このOTPはMFA認証時に使用される。
Returns:
int: ワンタイムパスワード
"""
# 1Passwordのインスタンスを作成
op = OnePassword()
# 1Password内のアイテムリストから、指定したタイトル(TITLE)に一致するアイテムを取得し、そのUUIDを取得
uuid = [item['id'] for item in op.list_items() if item['title'] == TITLE][0]
# 取得したUUIDを使って、OTP(ワンタイムパスワード)を取得
one_time_password = op.get_item_otp(uuid=uuid)
return one_time_password
この関数では、1PasswordのAPIを使って、指定されたタイトル(TITLE)に一致するアイテムからOTPを取得しています。これにより、1Passwordに保存されたMFAトークンを自動で取得できるため、手動でMFAコードを入力する手間が省けます。
MFAデバイスの取得
def get_mfa_device() -> str:
"""
IAMユーザーに紐づけられたMFAデバイスのシリアル番号を取得する関数。
このシリアル番号はMFA認証時に使用される。
Returns:
str: MFAデバイスのシリアル番号
"""
# IAMクライアントを作成
client = boto3.client("iam")
# 指定したIAMユーザーに紐づけられたMFAデバイスのリストを取得し、最初のデバイスのシリアル番号を取得
mfa_device = client.list_mfa_devices(UserName=IAM_USER_NAME)['MFADevices'][0]['SerialNumber']
return mfa_device
この関数では、指定されたIAMユーザーに紐づけられたMFAデバイスのシリアル番号を取得しています。MFAデバイスは、AWSのMFA認証時に必要な情報です。
AssumeRoleでのロール切り替え
def assume_role(one_time_password: int, mfa_device: str, TARGET_ROLE_ARN: str) -> Session:
"""
指定したロールにスイッチロール(Assume Role)する関数。
MFA認証を行い、一時的なセッションを取得する。
Args:
one_time_password (int): ワンタイムパスワード(OTP)
mfa_device (str): MFAデバイスのシリアル番号
TARGET_ROLE_ARN (str): スイッチロールするターゲットロールのARN
Returns:
Session: 一時的なセッション情報を持つboto3のSessionオブジェクト
"""
# STSクライアントを作成
client = boto3.client("sts")
# AssumeRole APIを呼び出して、一時的な認証情報を取得
temp_credentials = client.assume_role(
RoleArn=TARGET_ROLE_ARN, # スイッチするロールのARN
RoleSessionName='assume_role_sample', # セッションに付ける名前
SerialNumber=mfa_device, # MFAデバイスのシリアル番号
TokenCode=one_time_password # ワンタイムパスワード(OTP)
)
# 一時的な認証情報を使用して新しいセッションを作成
assume_role_session = Session(
aws_access_key_id=temp_credentials['Credentials']['AccessKeyId'],
aws_secret_access_key=temp_credentials['Credentials']['SecretAccessKey'],
aws_session_token=temp_credentials['Credentials']['SessionToken'],
region_name=TARGET_REGION, # 使用するリージョン
)
return assume_role_session
この関数では、AWS STSのassume_role APIを使用して、指定されたIAMロールにスイッチしています。MFA認証を行うために、MFAデバイスのシリアル番号とOTPを引数として渡しています。
S3バケットのリストを取得
def main():
"""
メイン処理を行う関数。
MFA認証を行い、指定のロールにスイッチロールした後、S3のバケットリストを表示する。
"""
# MFAデバイスのシリアル番号を取得
mfa_device = get_mfa_device()
# 1Passwordからワンタイムパスワードを取得
one_time_password = get_one_time_password()
# スイッチロールを行い、一時的なセッションを取得
assume_role_session = assume_role(one_time_password, mfa_device, TARGET_ROLE_ARN)
# スイッチロールしたセッションを使ってS3クライアントを作成
s3 = assume_role_session.client('s3')
# S3バケットのリストを取得して表示
print(s3.list_buckets())
main関数では、MFAデバイスとOTPを取得し、それらを使ってIAMロールにスイッチしています。最後に、スイッチしたセッションを使ってS3バケットのリストを取得しています。
セキュリティ上の考慮点
このスクリプトでは、MFA認証とAssumeRoleを組み合わせることで、セキュリティを強化しています。通常のアクセスキーやシークレットキーだけではなく、MFAデバイスとワンタイムパスワードを使用することで、不正アクセスのリスクを大幅に減少させることができます。
また、1Passwordを使用してOTPを自動的に取得することで、セキュリティを保ちながら利便性を向上させています。
まとめ
今回紹介したスクリプトは、AWSのMFA認証とAssumeRoleを使って安全にS3バケットにアクセスするための手法を示しています。1Passwordを使ってOTPを自動的に取得することで、セキュリティを保ちながら利便性を向上させることができます。
AWSのセキュリティを強化し、MFA認証を効率的に扱いたい方にとって、このスクリプトは非常に有用なツールとなるでしょう。ぜひ、自分のプロジェクトに取り入れてみてください!